﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WeatherClockWidget;
using System.Globalization;
using WeatherClockWidget.Domain;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;

namespace Foreca
{
    public class Weather : IWeatherProvider
    {
        const String prefix = "foreca";
        Regex rePrefix = new Regex(prefix + "\\|(.+)$");
        NumberFormatInfo nfi = new NumberFormatInfo() { CurrencyDecimalSeparator = "." };
        #region GetCoordinates
        public WeatherClockWidget.Domain.Coordinates GetCoordinates(string locationCode)
        {
            var coord = new Coordinates() { X = 0, Y = 0 };
            //try { } catch (Exception er) { HTCHome.Core.Logger.Log("Failed to get coordinates!\r\n" + er.ToString()); }
            return coord;
        }
        #endregion
        #region GetLocation
        public List<WeatherClockWidget.Domain.CityLocation> GetLocation(string query)
        {
            if (!String.IsNullOrEmpty(query) && !String.IsNullOrEmpty(query.Trim()))
            {
                String Query = query.Trim();
                try
                {
                    HttpWebRequest request = null;
                    HttpWebResponse response = null;
                    byte[] bytes = new byte[0];
                    Stream os = null;

                    Uri url = new Uri("http://www.foreca.com");
                    bool isEng = true;
                    string result = String.Empty;
                    string resultEn = String.Empty;
                    CultureInfo culture = new CultureInfo(Widget.LocaleManager.LocaleCode);
                    isEng = culture.TwoLetterISOLanguageName.ToLower() == "en";
                    url = new Uri(GetLanguageUrl(culture));
                    #region language cookie
                    if (!isEng)
                        try
                        {
                            request = (HttpWebRequest)WebRequest.Create(url);
                            request.SendChunked = false;
                            request.Proxy = HTCHome.Core.GeneralHelper.Proxy != null ? HTCHome.Core.GeneralHelper.Proxy : WebRequest.GetSystemWebProxy();
                            request.Timeout = 15000;
                            request.UserAgent = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 8.0) (compatible; MSIE 8.0; Windows NT 5.1;)";
                            request.CookieContainer = new CookieContainer();
                            request.AllowAutoRedirect = true;
                            response = (HttpWebResponse)request.GetResponse();
                        }
                        catch { isEng = true; }
                    #endregion
                    Uri hostUrl = new Uri(url.Scheme + "://" + url.Host + ":" + url.Port.ToString() + "/complete.php");
                    #region Search locations
                    #region languge result
                    if (!isEng)
                    {
                        HttpWebRequest request1 = (HttpWebRequest)WebRequest.Create(hostUrl);
                        request1.SendChunked = false;
                        request1.Proxy = HTCHome.Core.GeneralHelper.Proxy != null ? HTCHome.Core.GeneralHelper.Proxy : WebRequest.GetSystemWebProxy();
                        request1.Timeout = 15000;
                        request1.UserAgent = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 8.0) (compatible; MSIE 8.0; Windows NT 5.1;)";
                        request1.Method = "POST";
                        request1.ContentType = "application/x-www-form-urlencoded";
                        request1.CookieContainer = new CookieContainer();
                        if (response != null)
                            foreach (Cookie c in response.Cookies)
                                request1.CookieContainer.Add(c);
                        bytes = Encoding.ASCII.GetBytes("q=" + Helper.UrlEncode(Query));
                        try
                        {
                            request1.ContentLength = bytes.Length;
                            os = request1.GetRequestStream();
                            os.Write(bytes, 0, bytes.Length);
                            os.Close();
                            HttpWebResponse response1 = (HttpWebResponse)request1.GetResponse();
                            result = new StreamReader(response1.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                        }
                        catch { isEng = true; }
                        finally { }
                    }
                    #endregion
                    if (response != null)
                        response.Close();
                    response = null;
                    request = null;
                    #region english result
                    url = new Uri(GetLanguageUrl(new CultureInfo("en-US")));
                    hostUrl = new Uri(url.Scheme + "://" + url.Host + ":" + url.Port.ToString() + "/complete.php");
                    request = (HttpWebRequest)WebRequest.Create(hostUrl);
                    request.SendChunked = false;
                    request.Proxy = HTCHome.Core.GeneralHelper.Proxy != null ? HTCHome.Core.GeneralHelper.Proxy : WebRequest.GetSystemWebProxy();
                    request.Timeout = 15000;
                    request.UserAgent = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 8.0) (compatible; MSIE 8.0; Windows NT 5.1;)";
                    request.Method = "POST";
                    request.ContentType = "application/x-www-form-urlencoded";
                    bytes = Encoding.ASCII.GetBytes("q=" + Helper.UrlEncode(Query));
                    try
                    {
                        request.ContentLength = bytes.Length;
                        os = request.GetRequestStream();
                        os.Write(bytes, 0, bytes.Length);
                        os.Close();
                        response = (HttpWebResponse)request.GetResponse();
                        resultEn = new StreamReader(response.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                    }
                    catch { isEng = true; }
                    finally { }
                    #endregion
                    #region Clear streams
                    response.Close(); response = null;
                    request = null;
                    if (os != null) { os.Close(); os.Dispose(); os = null; }
                    #endregion
                    if (isEng || String.IsNullOrEmpty(result)) result = resultEn;
                    if (resultEn.IndexOf("<li style") > 0)
                        resultEn = resultEn.Substring(0, resultEn.IndexOf("<li style"));
                    Regex liRe = new Regex("<li id=\"(\\d+)\">(.+?)</li>", RegexOptions.IgnoreCase);
                    if (liRe.IsMatch(resultEn))
                    {
                        List<WeatherClockWidget.Domain.CityLocation> CityLocationLsit = new List<CityLocation>();
                        //List<LocationData> CityLocationLsit = new List<LocationData>();
                        #region Get list data
                        for (int mi = 0; mi < liRe.Matches(resultEn).Count; mi++)
                        {
                            Match mv = liRe.Matches(result)[mi];
                            String locName = new Regex("</?[^>]+>").Replace(mv.Groups[2].Value, String.Empty);
                            String locCountry = String.Empty;
                            if (locName.Split(',').Length > 1)
                            {
                                locCountry = locName.Substring(locName.IndexOf(',') + 1).Trim();
                                locName = locName.Split(',')[0].Trim();
                            }
                            if (locCountry.IndexOf(",") > 0)
                                locCountry = locCountry.Split(',')[1].Trim() + ", " + locCountry.Split(',')[0].Trim();
                            CityLocationLsit.Add(new CityLocation()
                            {
                                Code = prefix + "|" + mv.Groups[1].Value,
                                City = locName + " - " + locCountry
                            });
                        }
                        #endregion
                        return CityLocationLsit;
                    }
                    #endregion
                }
                catch (Exception er)
                {
                    HTCHome.Core.Logger.Log("Failed to get data on location!\r\n" + er.ToString());
                }
            }
            return null;
        }
        #endregion
        #region GetWeatherReport
        public WeatherClockWidget.Domain.WeatherReport GetWeatherReport(string locale, string locationcode, int degreeType)
        {
            bool isMetric = degreeType == 0;
            CultureInfo culture = new CultureInfo(Widget.LocaleManager.LocaleCode);
            Uri url = new Uri(GetLanguageUrl(culture));
            Uri hostUrl = new Uri(url.ToString() + (!String.IsNullOrEmpty(url.Query) ? "&" : "?") + "id=" + rePrefix.Match(locationcode).Groups[1].Value + "&quick_units=" + (isMetric ? "metric" : "us"));
            String CurentDataUrl = hostUrl.ToString();
            try
            {
                String CurentData = Helper.HtmlDecode(HTCHome.Core.GeneralHelper.GetXml(CurentDataUrl));
                if (string.IsNullOrEmpty(CurentData)) return null;
                #region Location name
                Regex CityNameRe = new Regex("<h1 class=\"entry-title\">([^<]+)</h1>", RegexOptions.IgnoreCase);
                if (CityNameRe.IsMatch(CurentData))
                    locale = CityNameRe.Match(CurentData).Groups[1].Value.Trim();
                else
                {
                    /*арабский*/
                    CityNameRe = new Regex("<div class=\"to-left\">\\s*<h1[^>]*>([^<]+)</h1>\\s*</div>", RegexOptions.IgnoreCase);
                    if (CityNameRe.IsMatch(CurentData))
                        locale = CityNameRe.Match(CurentData).Groups[1].Value.Trim();
                    else
                    {
                        CityNameRe = new Regex("<div class=\"to-left\">\\s*<h1[^>]*>([^<]+)</h1>\\s*</div>", RegexOptions.IgnoreCase);
                        if (CityNameRe.IsMatch(CurentData))
                            locale = CityNameRe.Match(CurentData).Groups[1].Value.Trim();
                    }
                }
                if (locale.IndexOf(",") > 0)
                    locale = locale.Split(',')[0].Trim();
                WeatherReport result = new WeatherReport();
                result.Location = locale;
                #endregion
                #region Curent data
                String CurentReStr = "<div class=\"left\">\\s*" +
                        "<div class=\"symbol_70x70\\w{1}\\s*symbol_([^_]+)[^>]*></div>\\s*" +
                        "<span[^>]*>\\s*<strong[^>]*>" +
                        "(.*?\\d+)</strong>.*?</span>\\s*<br[^>]*>\\s*" +
                        "(<img[^>]*>\\s*<strong[^>]*>\\s*(\\d+).*?</strong>\\s*<br[^>]*>)*\\s*" +
                        "</div>\\s*" +
                        "<div[^>]*>\\s*([^<]*)<br[^>]*>[^<]*<strong[^>]*>\\s*(.*?\\d+).*?</strong>\\s*<br[^>]*>\\s*" +
                        "[^<]*<strong[^>]*>[^<]*</strong>\\s*<br[^>]*>\\s*[^<]*<strong[^>]*>[^<]*</strong>\\s*<br[^>]*>\\s*" +
                        "[^<]*<strong[^>]*>(.*?)%\\s*</strong>";
                Regex CurentRe = new Regex(CurentReStr, RegexOptions.IgnoreCase);
                if (CurentRe.IsMatch(CurentData))
                {
                    result.NowSkyCode = GetWeatherPic(CurentRe.Match(CurentData).Groups[1].Value.Trim());
                    result.NowSky = CurentRe.Match(CurentData).Groups[5].Value.Trim();
                    result.Url = CurentDataUrl;
                    Int32.TryParse(CurentRe.Match(CurentData).Groups[2].Value.Trim(), out result.NowTemp);
                }
                else
                {
                    #region Arabic and old version
                    CurentReStr = "<div class=\"left\">\\s*" +
                        "<img class=\"symb\" src=\"/img/symb-70x70/([^\\.]+).png\"[^>]*>\\s*" +
                        "<span[^>]*>\\s*<strong[^>]*>" +
                        "(.*?\\d+)</strong>.*?</span>\\s*<br[^>]*>\\s*" +
                        "(<img[^>]*>\\s*<strong[^>]*>\\s*(\\d+).*?</strong>\\s*<br[^>]*>)*\\s*" +
                        "</div>\\s*" +
                        "<div[^>]*>\\s*" +
                        "([^<]*)<br[^>]*>" +
                        "[^<]*<strong[^>]*>\\s*(<span[^>]*>)*\\s*(.*?\\d+).*?(</span>)*\\s*</strong>\\s*<br[^>]*>\\s*" +
                        "[^<]*<strong[^>]*>[^<]*</strong>\\s*<br[^>]*>\\s*" +
                        "[^<]*<strong[^>]*>\\s*(<span[^>]*>)*[^<]*(</span>)*\\s*</strong>\\s*<br[^>]*>\\s*" +
                        "[^<]*<strong[^>]*>(.*?)%\\s*</strong>";

                    CurentRe = new Regex(CurentReStr, RegexOptions.IgnoreCase);
                    if (CurentRe.IsMatch(CurentData))
                    {
                        result.NowSkyCode = GetWeatherPic(CurentRe.Match(CurentData).Groups[1].Value.Trim());
                        result.NowSky = CurentRe.Match(CurentData).Groups[5].Value.Trim();
                        result.Url = CurentDataUrl;
                        Int32.TryParse(CurentRe.Match(CurentData).Groups[2].Value.Trim(), out result.NowTemp);
                    }
                    else return null;
                    #endregion
                }
                #endregion
                #region Forecast
                String ForecaDataUrl = hostUrl.ToString() + "&tenday";
                String ForecaData = Helper.HtmlDecode(HTCHome.Core.GeneralHelper.GetXml(ForecaDataUrl));
                Regex ForecastRe = new Regex("<div class=\"symbol_50x50d symbol_([^_]+)_50x50\"\\s*alt=\"([^\\\"]*)\"[^>]*></div>\\s*<br[^>]*>\\s*[^:]+:\\s*<strong>([^°]+)°</strong><br[^>]*>\\s*[^:]+:\\s*<strong>([^°]+)°</strong><br[^>]*>", RegexOptions.IgnoreCase);
                if (ForecastRe.IsMatch(ForecaData))
                {
                    DateTime date = DateTime.Today;
                    Int32 num = 0;
                    foreach (Match m in ForecastRe.Matches(ForecaData))
                        if (num < 5)
                            result.Forecast.Add(new DayForecast()
                            {
                                SkyCode = GetWeatherPic(m.Groups[1].Value.Trim()),
                                Text = m.Groups[2].Value.Trim(),
                                HighTemperature = Convert.ToInt32(m.Groups[3].Value.Trim()),
                                LowTemperature = Convert.ToInt32(m.Groups[4].Value.Trim()),
                                Url = hostUrl.ToString() + "&details=" + DateTime.Today.AddDays(num++).ToString("yyyyMMdd")
                            });
                    if (result.Forecast.Count > 0)
                    {
                        result.High = result.Forecast[0].HighTemperature;
                        result.Low = result.Forecast[0].LowTemperature;
                        return result;
                    }
                }
                else
                {
                    ForecastRe = new Regex("<img src=\"/img/symb-50x50/([^\\.]+).png\"\\s*alt=\"([^\\\"]*)\"[^>]*><br />\\s*" +
                        "[^:]+:\\s*<strong>([^°]+)°</strong>\\s*(</abbr>)*\\s*<br />\\s*[^:]+:\\s*<strong>([^°]+)°</strong>\\s*(</abbr>)*\\s*<br />\\s*", RegexOptions.IgnoreCase);
                    if (ForecastRe.IsMatch(ForecaData))
                    {
                        DateTime date = DateTime.Today;
                        Int32 num = 0;
                        foreach (Match m in ForecastRe.Matches(ForecaData))
                            if (num < 5)
                                result.Forecast.Add(new DayForecast()
                                {
                                    SkyCode = GetWeatherPic(m.Groups[1].Value.Trim()),
                                    Text = m.Groups[2].Value.Trim(),
                                    HighTemperature = Convert.ToInt32(m.Groups[3].Value.Trim()),
                                    LowTemperature = Convert.ToInt32(m.Groups[5].Value.Trim()),
                                    Url = hostUrl.ToString() + "&details=" + DateTime.Today.AddDays(num++).ToString("yyyyMMdd")
                                });
                        if (result.Forecast.Count > 0)
                        {
                            result.High = result.Forecast[0].HighTemperature;
                            result.Low = result.Forecast[0].LowTemperature;
                            return result;
                        }
                    }
                }
                #endregion
            }
            catch (Exception er)
            {
                HTCHome.Core.Logger.Log("Failed to get the forecast!\r\n" + er.ToString());
            }
            return null;
        }
        #endregion
        #region GetWeatherPic
        private int GetWeatherPic(string icon)
        {
            try
            {
                //Облачность 0-Ясно, 1-Небольшая облачность, 2-Переменная облачность, 3-Облачно с прояснениями, 4-Облачно
                //Сила осадков 0-нет, 1-слабый, 2-тип, 3-сильный, 4-гроза
                //Тип осадков 0-дождь,1-осадки 2-снег
                bool isDay = icon.ToLower()[0] == 'd';
                switch (Convert.ToInt32(icon.Substring(1)))
                {
                    case 0: return isDay ? 1 : 33; //Ясно
                    case 100: return isDay ? 2 : 34; //Небольшая облачность
                    case 200: return isDay ? 3 : 35; //Переменная облачность
                    case 210: return isDay ? 14 : 39; //Переменная облачность, слабый дождь
                    case 211: return isDay ? 26 : 26; //Переменная облачность, слабые осадки
                    case 212: return isDay ? 21 : 44; //Переменная облачность, слабый снег
                    case 220: return isDay ? 14 : 39; //Переменная облачность, дождь
                    case 221: return isDay ? 26 : 26; //Переменная облачность, осадки
                    case 222: return isDay ? 20 : 43; //Переменная облачность, снег
                    case 240: return isDay ? 17 : 41; //Переменная облачность, гроза
                    case 300: return isDay ? 6 : 38; //Облачно с прояснениями
                    case 310: return isDay ? 13 : 40; //Облачно с прояснениями, слабый дождь
                    case 311: return isDay ? 26 : 26; //Облачно с прояснениями, слабый осадки
                    case 312: return isDay ? 21 : 44; //Облачно с прояснениями, слабый снег
                    case 320: return isDay ? 13 : 40; //Облачно с прояснениями, дождь
                    case 321: return isDay ? 26 : 26; //Облачно с прояснениями, осадки
                    case 322: return isDay ? 20 : 43; //Облачно с прояснениями, снег
                    case 340: return isDay ? 16 : 42; //Облачно с прояснениями, гроза
                    case 400: return isDay ? 7 : 8; //Облачно
                    case 410: return isDay ? 12 : 12; //Облачно, слабый дождь
                    case 411: return isDay ? 26 : 26; //Облачно, слабый осадки
                    case 412: return isDay ? 24 : 24; //Облачно, слабый снег
                    case 420: return isDay ? 12 : 12; //Облачно, дождь
                    case 421: return isDay ? 26 : 26; //Облачно, осадки
                    case 422: return isDay ? 19 : 19; //Облачно, снег
                    case 430: return isDay ? 18 : 18; //Облачно, сильный дождь
                    case 431: return isDay ? 26 : 26; //Облачно, сильные осадки
                    case 432: return isDay ? 22 : 22; //Облачно, сильный снег
                    case 440: return isDay ? 15 : 15; //Облачно, гроза
                    default: return 1;
                }
            }
            catch { return 1; }
        }
        #endregion
        #region GetLanguageUrl
        String GetLanguageUrl(CultureInfo LCode)
        {
            try
            {
                if (LCode == new CultureInfo("pt-pt"))
                    return "http://www.foreca.com/?lang=pt_pt";             //Português europeu
                if (LCode == new CultureInfo("pt-br"))
                    return "http://www.foreca.com/?lang=pt_br";             //Português brasileiro
                switch (LCode.TwoLetterISOLanguageName.ToLower())
                {
                    //case "ru": return "http://foreca.com/?lang=ru";       //Русский
                    case "ru": return "http://foreca.ru/";                  //Русский
                    case "ar": return "http://foreca.com/?lang=ar";         //الْعَرَبيّة
                    case "bn": return "http://foreca.in/?lang=bn";          //বাংলা
                    case "bg": return "http://foreca.in/?lang=bg";          //български
                    case "cs": return "http://foreca.cz/?lang=cs";          //Čeština
                    case "da": return "http://foreca.dk/?lang=da";          //Dansk
                    case "de": return "http://foreca.de/?lang=de";          //Deutsch
                    case "et": return "http://foreca.com/?lang=et";         //Eesti
                    case "el": return "http://foreca.gr/?lang=el";          //Ελληνικά
                    case "es": return "http://foreca.es/?lang=es";          //Español
                    case "fr": return "http://foreca.com/?lang=fr";         //Français
                    case "gu": return "http://foreca.in/?lang=gu";          //ગુજરાતી
                    case "hi": return "http://foreca.in/?lang=hi";          //हिन्दी
                    case "hr": return "http://foreca.com/?lang=hr";         //Hrvatski
                    case "kn": return "http://foreca.in/?lang=kn";          //ಕನ್ನಡ
                    case "lt": return "http://foreca.com/?lang=lt";         //Lietuvių
                    case "hu": return "http://foreca.hu/?lang=hu";          //Magyar
                    case "ml": return "http://foreca.in/?lang=ml";          //മലയാളം
                    case "is": return "http://foreca.com/?lang=is";         //Íslenska
                    case "it": return "http://foreca.it/?lang=it";          //Italiano
                    case "nl": return "http://foreca.nl/?lang=nl";          //Nederlands
                    case "ja": return "http://foreca.com/?lang=ja";         //日本語
                    case "pl": return "http://foreca.pl/?lang=pl";          //Polski
                    case "pa": return "http://foreca.in/?lang=pa";          //ਪੰਜਾਬੀ
                    case "ro": return "http://foreca.ro/?lang=ro";          //Româneşte
                    case "sr": return "http://foreca.com/?lang=sr";         //Srpski
                    case "fi": return "http://foreca.fi";                   //Suomi
                    case "sv": return "http://foreca.se/?lang=sv";          //Svenska
                    case "ta": return "http://foreca.in/?lang=ta";          //தமிழ்
                    case "te": return "http://foreca.in/?lang=te";          //తెలుగు
                    case "tr": return "http://foreca.com/?lang=tr";         //Türkçe
                    case "th": return "http://foreca.com/?lang=th";         //ไทย
                    case "uk": return "http://foreca.com/?lang=uk";         //Українська
                    case "sq": return "http://foreca.com/?lang=sq";         //Shqip
                    case "lv": return "http://foreca.lv";                   //Latviešu
                    default: return "http://foreca.com/?lang=en";           //English
                }
            }
            catch { return "http://foreca.com/?lang=en"; }
        }
        #endregion
    }
}
